#!/bin/bash

# network_traffic.sh [-tPOLLING_INTERVAL] [NETWORK_INTERFACE...]

getopts t: __ && shift
isecs=${OPTARG:-1}
ifaces=($@)
: ${rate_max:=1000000} # maximum transfer rate for {percent}, can be overridden setting the env var

# `snore` adapted from https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever
# without MacOS workaround, TODO: with _snore_fd initialized separatedly, also i dont touch IFS so dont bother with it
snore() {
	local IFS
	[[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null
	read ${1:+-t "$1"} -u $_snore_fd || :
}

human_readable() {
	local hrunits=(B K M G T P)
	local ndigits=${#1}
	local idxunit=$(((2 + ndigits) / 3 - 1))
	local lentrim=$((ndigits - (idxunit * 3)))
	echo ${1::$lentrim}${hrunits[$idxunit]}
}

exit_err() {
	printf '{"text": "⚠ %s", "tooltip": "%s", "class": "error"}\n' "$@"
	exit
}

if test ${#ifaces[@]} -gt 0; then
	# sanity check the interface names
	for iface in ${ifaces[@]}; do
		test -h "/sys/class/net/${iface}" || exit_err "${iface}" "${iface} is not an existing network interface name"
	done
else
	# default to all interfaces except `lo`
	ifaces=(/sys/class/net/*)
	ifaces=(${ifaces[@]##*/})
	ifaces=(${ifaces[@]//lo/})
# TODO: check that filtering out `lo` is enough, else `^(eth|wlan|enp|wlp)` as suggested
fi

# sanity check polling interval
if test ${isecs} -lt 1; then
	exit_err "${isecs}" "${isecs} is not a valid polling interval"
fi
# NOTE: `snore` would take a decimal interval but bash arithmetic does not
#if test $(echo "${isecs} >= 0.2" |bc) -eq 0; then
#  exit_err "${isecs}" "${isecs} is not a valid polling interval"
#fi

# NOTE: `/proc/net/dev` format is:
#   interface:
#       RX  bytes packets errs drop fifo  frame compressed multicast
#       TX  bytes packets errs drop fifo  colls carrier compressed

# NOTE: array items are:
# 0=rx_bytes 1=rx_packets 2=rx_errs 3=rx_drop
# 4=tx_bytes 5=tx_packets 6=tx_errs 7=tx_drop
for iface in ${ifaces[@]} aggregate; do
	# declare -a traffic_prev_${iface} traffic_curr_${iface} traffic_delt_${iface}
	# declare -n traffic_prev=traffic_prev_${iface}
	# declare -n traffic_curr=traffic_curr_${iface}
	# declare -n traffic_delt=traffic_delt_${iface}
	traffic_prev=(0 0 0 0 0 0 0 0)
	traffic_curr=(0 0 0 0 0 0 0 0)
	traffic_delt=(0 0 0 0 0 0 0 0)
done

# TODO: rearrange the loop, do not show bogus on first iteration
while snore ${isecs}; do
	tooltip=""
	traffic_delt_aggregate=(0 0 0 0 0 0 0 0)

	readarray -s2 proc_net_dev </proc/net/dev
	while read -a data; do
		iface=${data[0]%:}
		test "${ifaces[*]}" = "${ifaces[*]//${iface}/}" && continue
		# declare -n traffic_prev=traffic_prev_${iface}
		# declare -n traffic_curr=traffic_curr_${iface}
		# declare -n traffic_delt=traffic_delt_${iface}
		traffic_curr=(${data[@]:1:4} ${data[@]:9:4})
		#FIXME: tooltip is delayed one iteration wrt main display (but why?)
		#printf -v tooltip_hr_rx '%4s⇣' $(human_readable ${traffic_delt[0]})
		#printf -v tooltip_hr_tx '%4s⇡' $(human_readable ${traffic_delt[4]})
		#tooltip="${tooltip:+${tooltip}\r}${iface}\t${tooltip_hr_rx}\t${tooltip_hr_tx}"
		for i in {0..7}; do
			((traffic_delt[i] = (traffic_curr[i] - traffic_prev[i]) / isecs))
			((traffic_delt_aggregate[i] += traffic_delt[i]))
		done
		traffic_prev=(${traffic_curr[@]})
	done <<<"${proc_net_dev[@]}"

	printf '{"text": "⇣ %-4s ⇡ %-4s", "tooltip": "%s",  "percentage": %d}\n' \
		$(human_readable ${traffic_delt_aggregate[0]}) \
		$(human_readable ${traffic_delt_aggregate[4]}) \
		"${tooltip}" \
		$(((traffic_delt_aggregate[0] + traffic_delt_aggregate[4]) / rate_max))
	#printf '{"text": "⇣%5s ⇡%5s", "alt": "%s", "tooltip": "%s", "class": "%s", "percentage": %d }\n' $(human_readable ${traffic_delt[0]}) $(human_readable ${traffic_delt[4]}) '_alt' '_tooltip' '_class' 0
done

# NOTE: in waybar config (do NOT use "interval"):
#         "custom/network_traffic": {
#             "exec": "~/.config/waybar/scripts/network_traffic.sh",
#             "return-type": "json",
#             "format": "Speed: {}",    // optional
#         },
